package org.jetlinks.pro.auth.wechat.miniprogram;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import lombok.Setter;
import org.hswebframework.web.exception.BusinessException;
import org.jetlinks.pro.ValueObject;
import org.jetlinks.pro.auth.sso.ThirdPartyProvider;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.net.URI;

/**
 * <img src="https://res.wx.qq.com/wxdoc/dist/assets/img/api-login.2fcc9f35.jpg">
 * <br>
 * 1. 小程序内部调用 wx.login() 获取 临时登录凭证code。<br>
 * 2. 然后调用接口GET /sso/{id}/notify-result?code=第1步获取到的code<br>
 * 3. 接口返回:
 * <pre>
 *
 *  {
 *       //是否依据绑定了用户,如果为false,则需要引导到登录页面
 *       //调用登录接口authorize/login时除了传入用户名密码外,还要传入参数bindCode:绑定码.
 *      "bound":false
 *      "bindCode":"绑定码",
 *
 *      "token":"当bound为true时表示当前用户的token,后续接口请求使用此token",
 *      "userId":"当bound为true时表示当前用户的ID"
 *  }
 * </pre>
 *
 * @author zhouhao
 * @since 1.12
 */
public class MiniProgramSsoProvider implements ThirdPartyProvider {

    public static final String API = "https://api.weixin.qq.com/sns/jscode2session";

    @Getter
    @Setter
    private String id;

    @Getter
    @Setter
    private String name;

    @Getter
    @Setter
    private String appId;

    @Getter
    @Setter
    private String secret;

    private WebClient client;

    public void init(WebClient.Builder builder) {
        this.client = builder.build();
    }

    @Override
    public URI getLoginUrl(@Nullable String redirect) {
        throw new UnsupportedOperationException("请从小程序中登录");
    }

    /**
     * https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/login/auth.code2Session.html
     *
     * @param parameter 参数信息
     * @return NotifyResult
     */
    @Override
    public Mono<NotifyResult> handleNotify(@Nonnull ValueObject parameter) {
        String code = parameter
            .getString("code")
            .orElseThrow(() -> new IllegalArgumentException("code can not be null"));

        return client
            .get()
            .uri(API, uriBuilder -> uriBuilder
                .queryParam("appid", appId)
                .queryParam("secret", secret)
                .queryParam("js_code", code)
                .queryParam("grant_type", "authorization_code")
                .build())
            .retrieve()
            .bodyToMono(String.class)
            .map(json -> {
                JSONObject response = JSON.parseObject(json);
                if (response.getIntValue("errcode") == 0) {
                    String openId = response.getString("openid");
                    String sessionKey = response.getString("session_key");
                    NotifyResult result = new NotifyResult();
                    result.setThirdPartyUserId(openId);
                    result.setDescription("微信小程序");

                    result.withOther("sessionKey", sessionKey)
                          .withOther("unionid", response.getString("unionid"));

                    return result;
                }
                throw new BusinessException(response.getString("errmsg"));
            });
    }
}
